/*
 * Copyright (C) 2011-2021 Intel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Intel Corporation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

// This sample is confined to the communication between a SGX client platform
// and an ISV Application Server.



#include <stdio.h>
#include <limits.h>
#include <unistd.h>
// Needed for definition of remote attestation messages.
#include "remote_attestation_result.h"

#include "isv_enclave_u.h"

// Needed to call untrusted key exchange library APIs, i.e. sgx_ra_proc_msg2.
#include "sgx_ukey_exchange.h"

// Needed to get service provider's information, in your real project, you will
// need to talk to real server.
#include "network_ra.h"

// Needed to create enclave and do ecall.
#include "sgx_urts.h"

// Needed to query extended epid group id.
#include "sgx_uae_epid.h"
#include "sgx_uae_quote_ex.h"

#include "service_provider.h"

#ifndef SAFE_FREE
#define SAFE_FREE(ptr) {if (NULL != (ptr)) {free(ptr); (ptr) = NULL;}}
#endif

// In addition to generating and sending messages, this application
// can use pre-generated messages to verify the generation of
// messages and the information flow.
#include "sample_messages.h"


#define ENCLAVE_PATH "isv_enclave.signed.so"

uint8_t* msg1_samples[] = { msg1_sample1, msg1_sample2 };
uint8_t* msg2_samples[] = { msg2_sample1, msg2_sample2 };
uint8_t* msg3_samples[] = { msg3_sample1, msg3_sample2 };
uint8_t* attestation_msg_samples[] =
    { attestation_msg_sample1, attestation_msg_sample2};

// Some utility functions to output some of the data structures passed between
// the ISV app and the remote attestation service provider.
void PRINT_BYTE_ARRAY(
    FILE *file, void *mem, uint32_t len)
{
    if(!mem || !len)
    {
        fprintf(file, "\n( null )\n");
        return;
    }
    uint8_t *array = (uint8_t *)mem;
    fprintf(file, "%u bytes:\n{\n", len);
    uint32_t i = 0;
    for(i = 0; i < len - 1; i++)
    {
        fprintf(file, "0x%x, ", array[i]);
        if(i % 8 == 7) fprintf(file, "\n");
    }
    fprintf(file, "0x%x ", array[i]);
    fprintf(file, "\n}\n");
}


void PRINT_ATTESTATION_SERVICE_RESPONSE(
    FILE *file,
    ra_samp_response_header_t *response)
{
    if(!response)
    {
        fprintf(file, "\t\n( null )\n");
        return;
    }

    fprintf(file, "RESPONSE TYPE:   0x%x\n", response->type);
    fprintf(file, "RESPONSE STATUS: 0x%x 0x%x\n", response->status[0],
            response->status[1]);
    fprintf(file, "RESPONSE BODY SIZE: %u\n", response->size);

    if(response->type == TYPE_RA_MSG2)
    {
        sgx_ra_msg2_t* p_msg2_body = (sgx_ra_msg2_t*)(response->body);

        fprintf(file, "MSG2 gb - ");
        PRINT_BYTE_ARRAY(file, &(p_msg2_body->g_b), sizeof(p_msg2_body->g_b));

        fprintf(file, "MSG2 spid - ");
        PRINT_BYTE_ARRAY(file, &(p_msg2_body->spid), sizeof(p_msg2_body->spid));

        fprintf(file, "MSG2 quote_type : %hx\n", p_msg2_body->quote_type);

        fprintf(file, "MSG2 kdf_id : %hx\n", p_msg2_body->kdf_id);

        fprintf(file, "MSG2 sign_gb_ga - ");
        PRINT_BYTE_ARRAY(file, &(p_msg2_body->sign_gb_ga),
                         sizeof(p_msg2_body->sign_gb_ga));

        fprintf(file, "MSG2 mac - ");
        PRINT_BYTE_ARRAY(file, &(p_msg2_body->mac), sizeof(p_msg2_body->mac));

        fprintf(file, "MSG2 sig_rl - ");
        PRINT_BYTE_ARRAY(file, &(p_msg2_body->sig_rl),
                         p_msg2_body->sig_rl_size);
    }
    else if(response->type == TYPE_RA_ATT_RESULT)
    {
        sample_ra_att_result_msg_t *p_att_result =
            (sample_ra_att_result_msg_t *)(response->body);
        fprintf(file, "ATTESTATION RESULT MSG platform_info_blob - ");
        PRINT_BYTE_ARRAY(file, &(p_att_result->platform_info_blob),
                         sizeof(p_att_result->platform_info_blob));

        fprintf(file, "ATTESTATION RESULT MSG mac - ");
        PRINT_BYTE_ARRAY(file, &(p_att_result->mac), sizeof(p_att_result->mac));

        fprintf(file, "ATTESTATION RESULT MSG secret.payload_tag - %u bytes\n",
                p_att_result->secret.payload_size);

        fprintf(file, "ATTESTATION RESULT MSG secret.payload - ");
        PRINT_BYTE_ARRAY(file, p_att_result->secret.payload,
                p_att_result->secret.payload_size);
    }
    else
    {
        fprintf(file, "\nERROR in printing out the response. "
                       "Response of type not supported %d\n", response->type);
    }
}

// This sample code doesn't have any recovery/retry mechanisms for the remote
// attestation. Since the enclave can be lost due S3 transitions, apps
// susceptible to S3 transitions should have logic to restart attestation in
// these scenarios.
#define _T(x) x
int main(int argc, char* argv[])
{
    int ret = 0;
    ra_samp_request_header_t *p_msg0_full = NULL;
    ra_samp_response_header_t *p_msg0_resp_full = NULL;
    ra_samp_request_header_t *p_msg1_full = NULL;
    ra_samp_response_header_t *p_msg2_full = NULL;
    sgx_ra_msg3_t *p_msg3 = NULL;
    ra_samp_response_header_t* p_att_result_msg_full = NULL;
    sgx_enclave_id_t enclave_id = 0;
    int enclave_lost_retry_time = 1;
    int busy_retry_time = 4;
    sgx_ra_context_t context = INT_MAX;
    sgx_status_t status = SGX_SUCCESS;
    ra_samp_request_header_t* p_msg3_full = NULL;
    sgx_att_key_id_t selected_key_id = {0};

    int32_t verify_index = -1;
    int32_t verification_samples = sizeof(msg1_samples)/sizeof(msg1_samples[0]);

    FILE* OUTPUT = stdout;

#define VERIFICATION_INDEX_IS_VALID() (verify_index > 0 && \
                                       verify_index <= verification_samples)
#define GET_VERIFICATION_ARRAY_INDEX() (verify_index-1)

    if(argc > 1)
    {

        verify_index = atoi(argv[1]);

        if( VERIFICATION_INDEX_IS_VALID())
        {
            fprintf(OUTPUT, "\nVerifying precomputed attestation messages "
                            "using precomputed values# %d\n", verify_index);
        }
        else
        {
            fprintf(OUTPUT, "\nValid invocations are:\n");
            fprintf(OUTPUT, "\n\tisv_app\n");
            fprintf(OUTPUT, "\n\tisv_app <verification index>\n");
            fprintf(OUTPUT, "\nValid indices are [1 - %d]\n",
                    verification_samples);
            fprintf(OUTPUT, "\nUsing a verification index uses precomputed "
                    "messages to assist debugging the remote attestation "
                    "service provider.\n");
            return -1;
        }
    }

    int i = 2; // We will do it twice, the first time is ECDSA quoting, the second one is EPID quoting
    do
    {
        if (i == 2)
        {
            fprintf(OUTPUT, "\nFirst round, we will try ECDSA algorithm.\n");
        }
        else
        {
            fprintf(OUTPUT, "\nSecond round, we will try EPID algorithm.\n");
        }
        // Preparation for remote attestation by configuring extended epid group id.
        {
            uint32_t extended_epid_group_id = 0;
            ret = sgx_get_extended_epid_group_id(&extended_epid_group_id);
            if (SGX_SUCCESS != ret)
            {
                ret = -1;
                fprintf(OUTPUT, "\nError, call sgx_get_extended_epid_group_id fail [%s].",
                    __FUNCTION__);
                return ret;
            }
            fprintf(OUTPUT, "\nCall sgx_get_extended_epid_group_id success.");

            p_msg0_full = (ra_samp_request_header_t*)
                malloc(sizeof(ra_samp_request_header_t)
                +sizeof(uint32_t));
            if (NULL == p_msg0_full)
            {
                ret = -1;
                goto CLEANUP;
            }
            p_msg0_full->type = TYPE_RA_MSG0;
            p_msg0_full->size = sizeof(uint32_t);

            *(uint32_t*)((uint8_t*)p_msg0_full + sizeof(ra_samp_request_header_t)) = extended_epid_group_id;
            {

                fprintf(OUTPUT, "\nMSG0 body generated -\n");

                PRINT_BYTE_ARRAY(OUTPUT, p_msg0_full->body, p_msg0_full->size);

            }
            // The ISV application sends msg0 to the SP.
            // The ISV decides whether to support this extended epid group id.
            fprintf(OUTPUT, "\nSending msg0 to remote attestation service provider.\n");

            ret = ra_network_send_receive("http://SampleServiceProvider.intel.com/",
                p_msg0_full,
                &p_msg0_resp_full);
            if (ret != 0)
            {
                fprintf(OUTPUT, "\nError, ra_network_send_receive for msg0 failed "
                    "[%s].", __FUNCTION__);
                goto CLEANUP;
            }
            fprintf(OUTPUT, "\nSent MSG0 to remote attestation service.\n");

            ret = sgx_select_att_key_id(p_msg0_resp_full->body, p_msg0_resp_full->size, &selected_key_id);
            if(SGX_SUCCESS != ret)
            {
                ret = -1;
                fprintf(OUTPUT, "\nInfo, call sgx_select_att_key_id fail, current platform configuration doesn't support this attestation key ID. [%s]",
                        __FUNCTION__);
                goto CLEANUP;
            }
            fprintf(OUTPUT, "\nCall sgx_select_att_key_id success.");
        }
        // Remote attestation will be initiated if the ISV server challenges the ISV
        // app or if the ISV app detects it doesn't have the credentials
        // (shared secret) from a previous attestation required for secure
        // communication with the server.
        {
            // ISV application creates the ISV enclave.
            do
            {
                ret = sgx_create_enclave(_T(ENCLAVE_PATH),
                                         SGX_DEBUG_FLAG,
                                         NULL,
                                         NULL,
                                         &enclave_id, NULL);
                if(SGX_SUCCESS != ret)
                {
                    ret = -1;
                    fprintf(OUTPUT, "\nError, call sgx_create_enclave fail [%s].",
                            __FUNCTION__);
                    goto CLEANUP;
                }
                fprintf(OUTPUT, "\nCall sgx_create_enclave success.");

                ret = enclave_init_ra(enclave_id,
                                      &status,
                                      false,
                                      &context);
            //Ideally, this check would be around the full attestation flow.
            } while (SGX_ERROR_ENCLAVE_LOST == ret && enclave_lost_retry_time--);

            if(SGX_SUCCESS != ret || status)
            {
                ret = -1;
                fprintf(OUTPUT, "\nError, call enclave_init_ra fail [%s].",
                        __FUNCTION__);
                goto CLEANUP;
            }
            fprintf(OUTPUT, "\nCall enclave_init_ra success.");

            // isv application call uke sgx_ra_get_msg1
            p_msg1_full = (ra_samp_request_header_t*)
                          malloc(sizeof(ra_samp_request_header_t)
                                 + sizeof(sgx_ra_msg1_t));
            if(NULL == p_msg1_full)
            {
                ret = -1;
                goto CLEANUP;
            }
            p_msg1_full->type = TYPE_RA_MSG1;
            p_msg1_full->size = sizeof(sgx_ra_msg1_t);
            do
            {
                ret = sgx_ra_get_msg1_ex(&selected_key_id, context, enclave_id, sgx_ra_get_ga,
                                      (sgx_ra_msg1_t*)((uint8_t*)p_msg1_full
                                      + sizeof(ra_samp_request_header_t)));
                sleep(3); // Wait 3s between retries
            } while (SGX_ERROR_BUSY == ret && busy_retry_time--);
            if(SGX_SUCCESS != ret)
            {
                ret = -1;
                fprintf(OUTPUT, "\nError, call sgx_ra_get_msg1_ex fail [%s].",
                        __FUNCTION__);
                goto CLEANUP;
            }
            else
            {
                fprintf(OUTPUT, "\nCall sgx_ra_get_msg1_ex success.\n");

                fprintf(OUTPUT, "\nMSG1 body generated -\n");

                PRINT_BYTE_ARRAY(OUTPUT, p_msg1_full->body, p_msg1_full->size);

            }

            if(VERIFICATION_INDEX_IS_VALID())
            {

                memcpy_s(p_msg1_full->body, p_msg1_full->size,
                         msg1_samples[GET_VERIFICATION_ARRAY_INDEX()],
                         p_msg1_full->size);

                fprintf(OUTPUT, "\nInstead of using the recently generated MSG1, "
                                "we will use the following precomputed MSG1 -\n");

                PRINT_BYTE_ARRAY(OUTPUT, p_msg1_full->body, p_msg1_full->size);
            }


            // The ISV application sends msg1 to the SP to get msg2,
            // msg2 needs to be freed when no longer needed.
            // The ISV decides whether to use linkable or unlinkable signatures.
            fprintf(OUTPUT, "\nSending msg1 to remote attestation service provider."
                            "Expecting msg2 back.\n");


            ret = ra_network_send_receive("http://SampleServiceProvider.intel.com/",
                                          p_msg1_full,
                                          &p_msg2_full);

            if(ret != 0 || !p_msg2_full)
            {
                fprintf(OUTPUT, "\nError, ra_network_send_receive for msg1 failed "
                                "[%s].", __FUNCTION__);
                if(VERIFICATION_INDEX_IS_VALID())
                {
                    fprintf(OUTPUT, "\nBecause we are in verification mode we will "
                                    "ignore this error.\n");
                    fprintf(OUTPUT, "\nInstead, we will pretend we received the "
                                    "following MSG2 - \n");

                    SAFE_FREE(p_msg2_full);
                    ra_samp_response_header_t* precomputed_msg2 =
                        (ra_samp_response_header_t*)msg2_samples[
                            GET_VERIFICATION_ARRAY_INDEX()];
                    const size_t msg2_full_size = sizeof(ra_samp_response_header_t)
                                                  +  precomputed_msg2->size;
                    p_msg2_full =
                        (ra_samp_response_header_t*)malloc(msg2_full_size);
                    if(NULL == p_msg2_full)
                    {
                        ret = -1;
                        goto CLEANUP;
                    }
                    memcpy_s(p_msg2_full, msg2_full_size, precomputed_msg2,
                             msg2_full_size);

                    PRINT_BYTE_ARRAY(OUTPUT, p_msg2_full,
                                     (uint32_t)sizeof(ra_samp_response_header_t)
                                     + p_msg2_full->size);
                }
                else
                {
                    goto CLEANUP;
                }
            }
            else
            {
                // Successfully sent msg1 and received a msg2 back.
                // Time now to check msg2.
                if(TYPE_RA_MSG2 != p_msg2_full->type)
                {

                    fprintf(OUTPUT, "\nError, didn't get MSG2 in response to MSG1. "
                                    "[%s].", __FUNCTION__);

                    if(VERIFICATION_INDEX_IS_VALID())
                    {
                        fprintf(OUTPUT, "\nBecause we are in verification mode we "
                                        "will ignore this error.");
                    }
                    else
                    {
                        goto CLEANUP;
                    }
                }

                fprintf(OUTPUT, "\nSent MSG1 to remote attestation service "
                                "provider. Received the following MSG2:\n");
                PRINT_BYTE_ARRAY(OUTPUT, p_msg2_full,
                                 (uint32_t)sizeof(ra_samp_response_header_t)
                                 + p_msg2_full->size);

                fprintf(OUTPUT, "\nA more descriptive representation of MSG2:\n");
                PRINT_ATTESTATION_SERVICE_RESPONSE(OUTPUT, p_msg2_full);

                if( VERIFICATION_INDEX_IS_VALID() )
                {
                    // The response should match the precomputed MSG2:
                    ra_samp_response_header_t* precomputed_msg2 =
                        (ra_samp_response_header_t *)
                        msg2_samples[GET_VERIFICATION_ARRAY_INDEX()];
                    if(MSG2_BODY_SIZE !=
                        sizeof(ra_samp_response_header_t) + p_msg2_full->size ||
                        memcmp( precomputed_msg2, p_msg2_full,
                            sizeof(ra_samp_response_header_t) + p_msg2_full->size))
                    {
                        fprintf(OUTPUT, "\nVerification ERROR. Our precomputed "
                                        "value for MSG2 does NOT match.\n");
                        fprintf(OUTPUT, "\nPrecomputed value for MSG2:\n");
                        PRINT_BYTE_ARRAY(OUTPUT, precomputed_msg2,
                                         (uint32_t)sizeof(ra_samp_response_header_t)
                                         + precomputed_msg2->size);
                        fprintf(OUTPUT, "\nA more descriptive representation "
                                        "of precomputed value for MSG2:\n");
                        PRINT_ATTESTATION_SERVICE_RESPONSE(OUTPUT,
                                                           precomputed_msg2);
                    }
                    else
                    {
                        fprintf(OUTPUT, "\nVerification COMPLETE. Remote "
                                        "attestation service provider generated a "
                                        "matching MSG2.\n");
                    }
                }

            }

            sgx_ra_msg2_t* p_msg2_body = (sgx_ra_msg2_t*)((uint8_t*)p_msg2_full
                                          + sizeof(ra_samp_response_header_t));


            uint32_t msg3_size = 0;
            if( VERIFICATION_INDEX_IS_VALID())
            {
                // We cannot generate a valid MSG3 using the precomputed messages
                // we have been using. We will use the precomputed msg3 instead.
                msg3_size = MSG3_BODY_SIZE;
                p_msg3 = (sgx_ra_msg3_t*)malloc(msg3_size);
                if(NULL == p_msg3)
                {
                    ret = -1;
                    goto CLEANUP;
                }
                memcpy_s(p_msg3, msg3_size,
                         msg3_samples[GET_VERIFICATION_ARRAY_INDEX()], msg3_size);
                fprintf(OUTPUT, "\nBecause MSG1 was a precomputed value, the MSG3 "
                                "we use will also be. PRECOMPUTED MSG3 - \n");
            }
            else
            {
                busy_retry_time = 2;
                // The ISV app now calls uKE sgx_ra_proc_msg2,
                // The ISV app is responsible for freeing the returned p_msg3!!
                do
                {
                    ret = sgx_ra_proc_msg2_ex(&selected_key_id,
                                       context,
                                       enclave_id,
                                       sgx_ra_proc_msg2_trusted,
                                       sgx_ra_get_msg3_trusted,
                                       p_msg2_body,
                                       p_msg2_full->size,
                                       &p_msg3,
                                       &msg3_size);
                } while (SGX_ERROR_BUSY == ret && busy_retry_time--);
                if(!p_msg3)
                {
                    fprintf(OUTPUT, "\nError, call sgx_ra_proc_msg2_ex fail. "
                                    "p_msg3 = 0x%p [%s].", p_msg3, __FUNCTION__);
                    ret = -1;
                    goto CLEANUP;
                }
                if(SGX_SUCCESS != (sgx_status_t)ret)
                {
                    fprintf(OUTPUT, "\nError, call sgx_ra_proc_msg2_ex fail. "
                                    "ret = 0x%08x [%s].", ret, __FUNCTION__);
                    ret = -1;
                    goto CLEANUP;
                }
                else
                {
                    fprintf(OUTPUT, "\nCall sgx_ra_proc_msg2_ex success.\n");
                    fprintf(OUTPUT, "\nMSG3 - \n");
                }
            }

            PRINT_BYTE_ARRAY(OUTPUT, p_msg3, msg3_size);

            p_msg3_full = (ra_samp_request_header_t*)malloc(
                           sizeof(ra_samp_request_header_t) + msg3_size);
            if(NULL == p_msg3_full)
            {
                ret = -1;
                goto CLEANUP;
            }
            p_msg3_full->type = TYPE_RA_MSG3;
            p_msg3_full->size = msg3_size;
            if(memcpy_s(p_msg3_full->body, msg3_size, p_msg3, msg3_size))
            {
                fprintf(OUTPUT,"\nError: INTERNAL ERROR - memcpy failed in [%s].",
                        __FUNCTION__);
                ret = -1;
                goto CLEANUP;
            }

            // The ISV application sends msg3 to the SP to get the attestation
            // result message, attestation result message needs to be freed when
            // no longer needed. The ISV service provider decides whether to use
            // linkable or unlinkable signatures. The format of the attestation
            // result is up to the service provider. This format is used for
            // demonstration.  Note that the attestation result message makes use
            // of both the MK for the MAC and the SK for the secret. These keys are
            // established from the SIGMA secure channel binding.
            ret = ra_network_send_receive("http://SampleServiceProvider.intel.com/",
                                          p_msg3_full,
                                          &p_att_result_msg_full);
            if(ret || !p_att_result_msg_full)
            {
                ret = -1;
                fprintf(OUTPUT, "\nError, sending msg3 failed [%s].", __FUNCTION__);
                goto CLEANUP;
            }


            sample_ra_att_result_msg_t * p_att_result_msg_body =
                (sample_ra_att_result_msg_t *)((uint8_t*)p_att_result_msg_full
                                               + sizeof(ra_samp_response_header_t));
            if(TYPE_RA_ATT_RESULT != p_att_result_msg_full->type)
            {
                ret = -1;
                fprintf(OUTPUT, "\nError. Sent MSG3 successfully, but the message "
                                "received was NOT of type att_msg_result. Type = "
                                "%d. [%s].", p_att_result_msg_full->type,
                                 __FUNCTION__);
                goto CLEANUP;
            }
            else
            {
                fprintf(OUTPUT, "\nSent MSG3 successfully. Received an attestation "
                                "result message back\n.");
                if( VERIFICATION_INDEX_IS_VALID() )
                {
                    if(ATTESTATION_MSG_BODY_SIZE != p_att_result_msg_full->size ||
                        memcmp(p_att_result_msg_full->body,
                            attestation_msg_samples[GET_VERIFICATION_ARRAY_INDEX()],
                            p_att_result_msg_full->size) )
                    {
                        fprintf(OUTPUT, "\nSent MSG3 successfully. Received an "
                                        "attestation result message back that did "
                                        "NOT match the expected value.\n");
                        fprintf(OUTPUT, "\nEXPECTED ATTESTATION RESULT -");
                        PRINT_BYTE_ARRAY(OUTPUT,
                            attestation_msg_samples[GET_VERIFICATION_ARRAY_INDEX()],
                            ATTESTATION_MSG_BODY_SIZE);
                    }
                }
            }

            fprintf(OUTPUT, "\nATTESTATION RESULT RECEIVED - ");
            PRINT_BYTE_ARRAY(OUTPUT, p_att_result_msg_full->body,
                             p_att_result_msg_full->size);


            if( VERIFICATION_INDEX_IS_VALID() )
            {
                fprintf(OUTPUT, "\nBecause we used precomputed values for the "
                                "messages, the attestation result message will "
                                "not pass further verification tests, so we will "
                                "skip them.\n");
                goto CLEANUP;
            }

            // Check the MAC using MK on the attestation result message.
            // The format of the attestation result message is ISV specific.
            // This is a simple form for demonstration. In a real product,
            // the ISV may want to communicate more information.
            ret = verify_att_result_mac(enclave_id,
                    &status,
                    context,
                    (uint8_t*)&p_att_result_msg_body->platform_info_blob,
                    sizeof(ias_platform_info_blob_t),
                    (uint8_t*)&p_att_result_msg_body->mac,
                    sizeof(sgx_mac_t));
            if((SGX_SUCCESS != ret) ||
               (SGX_SUCCESS != status))
            {
                ret = -1;
                fprintf(OUTPUT, "\nError: INTEGRITY FAILED - attestation result "
                                "message MK based cmac failed in [%s].",
                                __FUNCTION__);
                goto CLEANUP;
            }

            bool attestation_passed = true;
            // Check the attestation result for pass or fail.
            // Whether attestation passes or fails is a decision made by the ISV Server.
            // When the ISV server decides to trust the enclave, then it will return success.
            // When the ISV server decided to not trust the enclave, then it will return failure.
            if(0 != p_att_result_msg_full->status[0]
               || 0 != p_att_result_msg_full->status[1])
            {
                fprintf(OUTPUT, "\nError, attestation result message MK based cmac "
                                "failed in [%s].", __FUNCTION__);
                attestation_passed = false;
            }

            // The attestation result message should contain a field for the Platform
            // Info Blob (PIB).  The PIB is returned by attestation server in the attestation report.
            // It is not returned in all cases, but when it is, the ISV app
            // should pass it to the blob analysis API called sgx_report_attestation_status()
            // along with the trust decision from the ISV server.
            // The ISV application will take action based on the update_info.
            // returned in update_info by the API.
            // This call is stubbed out for the sample.
            //
            // sgx_update_info_bit_t update_info;
            // ret = sgx_report_attestation_status(
            //     &p_att_result_msg_body->platform_info_blob,
            //     attestation_passed ? 0 : 1, &update_info);

            // Get the shared secret sent by the server using SK (if attestation
            // passed)
            if(attestation_passed)
            {
                ret = put_secret_data(enclave_id,
                                      &status,
                                      context,
                                      p_att_result_msg_body->secret.payload,
                                      p_att_result_msg_body->secret.payload_size,
                                      p_att_result_msg_body->secret.payload_tag);
                if((SGX_SUCCESS != ret)  || (SGX_SUCCESS != status))
                {
                    fprintf(OUTPUT, "\nError, attestation result message secret "
                                    "using SK based AESGCM failed in [%s]. ret = "
                                    "0x%0x. status = 0x%0x", __FUNCTION__, ret,
                                     status);
                    goto CLEANUP;
                }
            }
            fprintf(OUTPUT, "\nSecret successfully received from server.");
            fprintf(OUTPUT, "\nRemote attestation success!");
        }

    CLEANUP:
        // Clean-up
        // Need to close the RA key state.
        if(INT_MAX != context)
        {
            int ret_save = ret;
            ret = enclave_ra_close(enclave_id, &status, context);
            if(SGX_SUCCESS != ret || status)
            {
                ret = -1;
                fprintf(OUTPUT, "\nError, call enclave_ra_close fail [%s].",
                        __FUNCTION__);
            }
            else
            {
                // enclave_ra_close was successful, let's restore the value that
                // led us to this point in the code.
                ret = ret_save;
            }
            fprintf(OUTPUT, "\nCall enclave_ra_close success.");
        }

        sgx_destroy_enclave(enclave_id);


        ra_free_network_response_buffer(p_msg0_resp_full);
        p_msg0_resp_full = NULL;
        ra_free_network_response_buffer(p_msg2_full);
        p_msg2_full = NULL;
        ra_free_network_response_buffer(p_att_result_msg_full);
        p_att_result_msg_full = NULL;

        // p_msg3 is malloc'd by the untrusted KE library. App needs to free.
        SAFE_FREE(p_msg3);
        SAFE_FREE(p_msg3_full);
        SAFE_FREE(p_msg1_full);
        SAFE_FREE(p_msg0_full);
    }while(--i);
    printf("\nEnter a character before exit ...\n");
    getchar();
    return ret;
}
